#include <stdio.h>
#include <malloc.h>
#include "declarations.h"
#include "grammar.tab.h"
#include "structs.h"
extern int yylex(void);

//!-- EXPR
// Очищаем структуру Expression
void CleanStructExpression(struct Expression * expr)
{
	expr->num = 0;
	expr->name = NULL;
	expr->name2 = NULL;
	expr->string = NULL;
	expr->left = NULL;
	expr->right = NULL;
	expr->arguments = NULL;
	expr->next = NULL;
}


//создание выражение для id
struct Expression * CreateIdentifierExpr(char * id)
{
	struct Expression *result = (struct Expression *)malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeIdentifier;
	result->name = id;
	return result;
}

// создание выражения Const
struct Expression * CreateConstantExpr(int constant)
{
	struct Expression *result = (struct Expression *)malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeConst;
	result->num = constant;
	return result;
}

// создание выражения Char
struct Expression * CreateCharExpr(char c)
{
	struct Expression *result = (struct Expression *)malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeChar;
	result->num = c;
	return result;
}

// создание выражения String
struct Expression * CreateStringExpr(char * string)
{
	struct Expression *result = (struct Expression *)malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeString;
	result->string = string;
	return result;
}

// бинарные операции
struct Expression * CreateBinaryExpr(enum ExprType type, struct Expression *left_expr, struct Expression *right_expr)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = type;
	result->left = left_expr;
	result->right = right_expr;
	return result;
}

// унарные операции
struct Expression * CreateUnaryExpr(enum ExprType type, struct Expression *expr)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = type;
	result->right = expr;
	return result;
}

// доступ к полю метода
struct Expression * CreateIntanceVariableAccessExpr(struct Expression * object, char * field)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeInstVarAccess;
	result->left = object;
	result->name2 = field;
	return result;
}

// вызов функции
struct Expression * CreateFunctionCallExpr(char * function_name, struct ExpressionList * args)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeFuncCall;
	result->name = function_name;
	result->arguments = args;
	return result;
}

// сообщения
struct Expression * CreateKeywordMessageExpr(char * selector_name, struct Expression * expr)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeKeywordMessage;
	result->name = selector_name;
	result->right = expr;
	return result;
}

// создание NEW
struct Expression * CreateNewExpr(char *id)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeNew;
	result->name = id;
	return result;
}

//вызов метода
struct Expression * CreateMethodCallExpr(enum CallObjectType object_type, struct Expression * object, char * method_name)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	result->type = ExprTypeMethodCall;
	result->callobjecttype = object_type;
	result->left = object;
	result->name2 = method_name;
	return result;
}

//вызов метода с параметрами
struct Expression * CreateMethodCallArgsExpr(enum CallObjectType object_type, struct Expression * object, struct ExpressionList * arguments)
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeMethodCallArgs;
	result->callobjecttype = object_type;
	result->left = object;
	result->arguments = arguments;
	return result;
}


//создание экспра на основе self
struct Expression * CreateSelfExpr()
{
	struct Expression *result = (struct Expression *) malloc(sizeof(struct Expression));
	CleanStructExpression(result);
	
	result->type = ExprTypeSelf;
	return result;
}

// создание списка аргументов
struct ExpressionList * CreateExpressionList(struct Expression * expr)
{
	struct ExpressionList *result=(struct ExpressionList *)malloc(sizeof(struct ExpressionList));
	result->first = NULL;
	result->last = NULL;

	if(expr != NULL)
	{
		result->first = expr;
		result->last = expr;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в список аргументов
struct ExpressionList * AddToExpressionList(struct ExpressionList * list, struct Expression * expr)
{
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(expr != NULL)
		{
			list->last->next = expr;
			list->last = expr;
			return list;
		}
	}
	else
		return CreateExpressionList(expr);
}

// создание списка сообщений
struct ExpressionList * CreateMessagesList(char * id, struct Expression * expr)
{
	struct ExpressionList *result=(struct ExpressionList *)malloc(sizeof(struct ExpressionList));
	struct Expression *message = CreateKeywordMessageExpr(id, expr);
	
	if(id != NULL)
	{
		result->first = message;
		result->last = message;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в список аргументов
struct ExpressionList * AddToMessagesList(struct ExpressionList * list, char * id, struct Expression * expr)
{
	struct Expression *message = CreateKeywordMessageExpr(id, expr);
	
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(id != NULL)
		{
			//заполняем новое сообщение			
			list->last->next = message;
			list->last = message;
			return list;
		}
	}
	else
		return CreateMessagesList(id, expr);
}

//!-- STMT
void CleanStructStatement(struct Statement * stmt)
{
    stmt->expr = NULL;
	stmt->precondition = NULL;
	stmt->condition = NULL;
	stmt->postcondition = NULL;
	stmt->stmt = NULL;
	stmt->falseStmt = NULL;
    stmt->block = NULL;
	stmt->labeledList = NULL;
	stmt->next = NULL;
}

struct Statement * CreateCompoundStatement(struct StatementList *list)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = StmtTypeCompound;
	result->block = list;
	
	return result;
}

struct Statement * CreateExprStatement(struct Expression *expr)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = StmtTypeExpr;
	result->expr = expr;
	
	return result;
}

struct Statement * CreateIfStatement(struct Expression *condition, struct Statement *true_stmt, struct Statement *false_stmt)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = StmtTypeIf;
	result->condition = condition;
	result->stmt = true_stmt;
	result->falseStmt = false_stmt;
	
	return result;
}

struct Statement * CreateSwitchStatement(struct Expression *condition, struct LabeledStatementList *labeled_list)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = StmtTypeSwitch;
	result->condition = condition;
	result->labeledList = labeled_list;
	
	return result;
}

struct Statement * CreateWhileStatement(enum StmtType type, struct Expression *condition, struct Statement *stmt)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = type;	//может быть while\dowhile
	result->condition = condition;
	result->stmt = stmt;
	
	return result;
}

struct Statement * CreateForStatement(struct Expression *precondition, struct Expression *condition, struct Expression *postcondition, struct Statement *stmt)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = StmtTypeFor;
	result->precondition = precondition;
	result->condition = condition;
	result->postcondition = postcondition;
	result->stmt = stmt;
	
	return result;
}

struct Statement * CreateJumpStatement(enum StmtType type, struct Expression *expr)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = type;	//может быть break\return
	result->expr = expr;
	
	return result;
}

//создать оператор из объявления
struct Statement * CreateVariableDeclarationStatement(struct VariableDeclaration * vardecl)
{
	struct Statement *result = (struct Statement *) malloc(sizeof(struct Statement));
	CleanStructStatement(result);
	
	result->type = StmtTypeVarDecl;	//может быть break\return
	result->vardecl = vardecl;
	
	return result;
}


void CleanLabeledStatement(struct LabeledStatement *lstmt)
{
	lstmt->num = 0;
	lstmt->list = NULL;
	lstmt->next = NULL;
}

struct LabeledStatement * CreateLabeledStatement(enum LabeledStatementType type, struct Expression * expr, struct StatementList * list)
{
	struct LabeledStatement *result = (struct LabeledStatement *) malloc(sizeof(struct LabeledStatement));
	CleanLabeledStatement(result);
	
	result->type = type;
	result->expr = expr;
	result->list = list;

	return result;
}

// создание списка
struct LabeledStatementList * CreateLabeledStatementList(struct LabeledStatement * stmt)
{
	struct LabeledStatementList *result=(struct LabeledStatementList *)malloc(sizeof(struct LabeledStatementList));
	result->first = NULL;
	result->last = NULL;

	if(stmt != NULL)
	{
		result->first = stmt;
		result->last = stmt;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в лист
struct LabeledStatementList * AddToLabeledStatementList(struct LabeledStatementList * list, struct LabeledStatement * stmt)
{
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(stmt != NULL)
		{
			list->last->next = stmt;
			list->last = stmt;
			return list;
		}
	}
	else
		return CreateLabeledStatementList(stmt);
}

// создание списка аргументов
struct StatementList * CreateStatementList(struct Statement *stmt)
{
	struct StatementList *result=(struct StatementList *)malloc(sizeof(struct StatementList));
	result->first = NULL;
	result->last = NULL;

	if(stmt != NULL)
	{
		result->first = stmt;
		result->last = stmt;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в список аргументов
struct StatementList * AddToStatementList(struct StatementList * list, struct Statement *stmt)
{
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(stmt != NULL)
		{
			list->last->next = stmt;
			list->last = stmt;
			return list;
		}
	}
	else
		return CreateStatementList(stmt);
}

//объявление enum'а
struct EnumDeclaration * CreateEnumDeclaration(char *id, struct EnumeratorList * list)
{
	struct EnumDeclaration * result = (struct EnumDeclaration *)malloc(sizeof(struct EnumDeclaration));
	result->type = CreateType(TypeVariantEnum, id);
	result->list = list;
	
	return result;
}

// создание списка вариантов Enum'а
struct EnumeratorList * CreateEnumeratorList(char * id)
{
	struct EnumeratorList *result = (struct EnumeratorList *)malloc(sizeof(struct EnumeratorList));
	result->first = NULL;
	result->last = NULL;
	
	if(id != NULL)
	{
		struct Enumerator *enumerator = (struct Enumerator *)malloc(sizeof(struct Enumerator));
		enumerator->id = id;
		enumerator->next = NULL;
		result->first = enumerator;
		result->last = enumerator;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в список вариантов enum'а
struct EnumeratorList * AddToEnumeratorList(struct EnumeratorList * list, char * id)
{
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(id != NULL)
		{
			struct Enumerator *enumerator = (struct Enumerator *)malloc(sizeof(struct Enumerator));
			enumerator->id = id;
			enumerator->next = NULL;
			list->last->next = enumerator;
			list->last = enumerator;
			return list;
		}
	}
	else
		return CreateEnumeratorList(id);
}

struct Type * CreateType(enum TypeVariant type, char * id)
{
	struct Type * result = (struct Type *)malloc(sizeof(struct Type));
	result->type = type;
	result->name = id;
	
	return result;
}

void CleanVariableDeclaration(struct VariableDeclaration * vardecl)
{
	vardecl->variable_type = NULL;
	vardecl->name = NULL;
	vardecl->size = 0;
	vardecl->partMethodName = NULL;
	vardecl->next = NULL;
}

// создание переменной
struct VariableDeclaration * CreateVariableDeclaration(enum VariableDeclarationType type, struct Type * variable_type, char *name, int size, char *part_name)
{
	struct VariableDeclaration * result = (struct VariableDeclaration *)malloc(sizeof(struct VariableDeclaration));
	CleanVariableDeclaration(result);
	
	result->type = type;
	result->variable_type = variable_type;
	result->name = name;
	result->size = size;
	result->partMethodName = part_name;
	
	return result;
}


// создание списка переменных
struct VariableDeclarationList * CreateVariableDeclarationList(struct VariableDeclaration * vardecl)
{
	struct VariableDeclarationList *result = (struct VariableDeclarationList *)malloc(sizeof(struct VariableDeclarationList));
	result->first = NULL;
	result->last = NULL;
	
	if(vardecl != NULL)
	{
		result->first = vardecl;
		result->last = vardecl;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в список переменных
struct VariableDeclarationList * AddToVariableDeclarationList(struct VariableDeclarationList * list, struct VariableDeclaration * vardecl)
{
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(vardecl != NULL)
		{
			list->last->next = vardecl;
			list->last = vardecl;
			return list;
		}
	}
	else
		return CreateVariableDeclarationList(vardecl);
}

struct Declarator * CreateDeclarator(enum DeclaratorType type, char *id, struct VariableDeclarationList * list)
{
	struct Declarator *result = (struct Declarator *) malloc(sizeof(struct Declarator));
	result->id = NULL;
	result->keyword_list = NULL;
	
	result->type = type;
	result->id = id;
	result->keyword_list = list;
	return result;
}

void CleanDeclarationDefinition(struct DeclarationDefinition * decldef)
{
	decldef->typeName = NULL;
	decldef->name = NULL;
	decldef->compoundStmt = NULL;
	decldef->next = NULL;
}

struct DeclarationDefinition * CreateMethodDeclarationDefinition(enum DeclType type, struct Type *typeName, struct Declarator *name, struct Statement * compoundStmt)
{
	struct DeclarationDefinition *result = (struct DeclarationDefinition *) malloc(sizeof(struct DeclarationDefinition));
	CleanDeclarationDefinition(result);
	
	result->declType = type;
	result->typeName = typeName;
	result->name = name;
	result->compoundStmt = compoundStmt;
	return result;
}

struct FunctionDefinition * CreateFunctionDefinition(struct Type *type, char *name, struct VariableDeclarationList * parameters, struct Statement * compoundStmt)
{
	struct FunctionDefinition *result = (struct FunctionDefinition *) malloc(sizeof(struct FunctionDefinition));
	//очищаем элементы структуры
	result->type = NULL;
	result->name = NULL;
	result->parameters = NULL;
	result->compoundStmt = NULL;

	result->type = type;
	result->name = name;
	result->parameters = parameters;
	result->compoundStmt = compoundStmt;
	return result;
}


// создание списка методов
struct DeclarationDefinitionList * CreateDeclarationDefinitionList(struct DeclarationDefinition * decldef)
{
	struct DeclarationDefinitionList *result=(struct DeclarationDefinitionList *)malloc(sizeof(struct DeclarationDefinitionList));
	result->first = NULL;
	result->last = NULL;

	if(decldef != NULL)
	{
		result->first = decldef;
		result->last = decldef;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в список
struct DeclarationDefinitionList * AddToDeclarationDefinitionList(struct DeclarationDefinitionList * list, struct DeclarationDefinition * decldef)
{
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(decldef != NULL)
		{
			list->last->next = decldef;
			list->last = decldef;
			return list;
		}
	}
	else
		return CreateDeclarationDefinitionList(decldef);
}

struct ClassInfo * CreateClassInfo(enum ClassInfoType type, char * name, char * parent, struct VariableDeclarationList * instanceVariables, struct DeclarationDefinitionList * methods)
{
	struct ClassInfo * result = (struct ClassInfo *) malloc(sizeof(struct ClassInfo));
	result->name = NULL;
	result->parent = NULL;
	result->instanceVariables = NULL;
	result->methods = NULL;
	
	result->type = type;
	result->name = name;
	result->parent = parent;
	result->instanceVariables = instanceVariables;
	result->methods = methods;
	return result;
}

void CleanExternalDeclaration(struct ExternalDeclaration * result)
{
	result->function = NULL;
	result->classInfo = NULL;
	result->enumDeclaration = NULL;
	result->next = NULL;
}

struct ExternalDeclaration * CreateFuncExternalDeclaration(struct FunctionDefinition * func)
{
	struct ExternalDeclaration * result = (struct ExternalDeclaration *) malloc(sizeof(struct ExternalDeclaration));
	CleanExternalDeclaration(result);
	
	result->type = ExternalDeclarationFunction;
	result->function = func;
	return result;
}

struct ExternalDeclaration * CreateClassExternalDeclaration(enum ExternalDeclarationType type, struct ClassInfo * class_info)
{
	struct ExternalDeclaration * result = (struct ExternalDeclaration *) malloc(sizeof(struct ExternalDeclaration));
	CleanExternalDeclaration(result);
	
	result->type = type;
	result->classInfo = class_info;
	return result;
}

struct ExternalDeclaration * CreateEnumExternalDeclaration(struct EnumDeclaration * decl)
{
	struct ExternalDeclaration * result = (struct ExternalDeclaration *) malloc(sizeof(struct ExternalDeclaration));
	CleanExternalDeclaration(result);
	
	result->type = ExternalDeclarationEnum;
	result->enumDeclaration = decl;
	return result;
}

// создание списка 
struct ExternalDeclarationList * CreateExternalDeclarationList(struct ExternalDeclaration * decl)
{
	struct ExternalDeclarationList *result=(struct ExternalDeclarationList *)malloc(sizeof(struct ExternalDeclarationList));
	result->first = NULL;
	result->last = NULL;

	if(decl != NULL)
	{
		result->first = decl;
		result->last = decl;
		return result;
	}
	else
	{
		return NULL;
	}
}

// добавление в список
struct ExternalDeclarationList * AddToExternalDeclarationList(struct ExternalDeclarationList * list, struct ExternalDeclaration * decl)
{
	// если в списке уже чтото есть
	if(list != NULL)
	{
		// и есть что добавлять
		if(decl != NULL)
		{
			list->last->next = decl;
			list->last = decl;
			return list;
		}
	}
	else
		return CreateExternalDeclarationList(decl);
}